<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>函数柯里化</title>
</head>
<body>

  <script>
    // 函数柯里化指的是一种将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术。
    // 普通的add函数
    // function sum(x) {
    //   console.log(arguments)
    //   if(arguments.length == 2) {
    //     return arguments[0] + arguments[1]
    //   }
      
    //   return function(y) {
    //     return x + y
    //   }
    // }
    function curry(fn, currArgs) {
      // fn.length 函数的参数个数，几个参数就是用自带的
      // currArgs
      // console.log(fn, fn.length,  'fn第一个参数')
      console.log(currArgs, 'currArgs第二个参数')

      return function() {
        // Javascrip中每个函数都会有一个Arguments对象实例arguments，它引用着函数的实参，可以用数组下标的方式"[]"引用arguments的元素。
        // arguments.length为函数实参个数，arguments.callee引用函数自身。
        // 使用方法
        // 虽然arguments对象并不是一个数组，但是访问单个参数的方式与访问数组元素的方式相同 例如 arguments[0],arguments[1],。。。arguments[n]； 
        // arguments 就是函数的参数
        // arguments是一个对象而不是数组，最多算是一个伪数组，而且自身的原型链上也没有slice这个方法。
        // console.log( arguments instanceof Array )  // false
        // console.log( [] instanceof Array )

        console.log(arguments, arguments[0]+"arguments[0]",'arguments')
        // console.log([])

        // 1.
        // [].slice.call(arguments)能将具有length属性的对象转成数组。
        // slice() 如果未指定start和end，则返回整个数组。

        // 2.
        // call()函数用于调用当前函数functionObject，并可同时使用指定对象thisObj作为本次执行时functionObject函数内部的this指针引用。

        // 通过call显式绑定来实现arguments变相有slice这个方法。
        // 这就是说：Array.prototype.slice.call(arguments,0)这句里，就是把 arguments 当做当前对象
        // 也就是说 要调用的是 arguments 的slice 方法，后面的 参数 0 也就成了 slice 的第一个参数，slice(0)就是获取所有。
        let args = [].slice.call(arguments)  // 主要作用是  将具有length属性的对象转成数组，因为 arguments 是伪数组，其实原型是对象，没有slice 属性

        // 首次调用时， 若未提供最后一个参数currArgs, 则不用进行args 的拼接
        if(currArgs !== undefined) {
          args = args.concat(currArgs)
        }


        // open_id=oAfMV5noKUj5yTBMYc9MhJ_OQLgY 417
        // open_id=oAfMV5noKUj5yTBMYc9MhJ_OQLgY 282 


        console.log(args, '合并后的数组')

        // 递归调用
        if(args.length < fn.length) {
          return curry(fn, args)
        }

        // 递归出口
        return fn.apply(null, args);
      }
    }

    function add(a, b, c) {
      console.log(a + b + c)
      
    }    

    let fn = curry(add)

    // console.log(fn(1, 2, 3))
    console.log(fn("我是")("小王")("你是谁"))

    // fn(1, 2)(3)
    // fn(1)(2)(3)
    
    // console.log(sum(1))
    // console.log(sum(1)(2))

    // console.log(fnLen.length, '函数的长度')

    // function curry(fn, args) {
    //   let length = fn.length;

    //   console.log(length, '函数的长度')

    //   args = args || [];

    //   return function() {
    //     // 第一次执行时，定义一个数组专门用来存储所有的参数
    //     let subArgs = args.slice(0); // slice 返回新数组，slice(begin, end) , end不包括在里面
    //     // 拼接得到现有的所有参数
    //     console.log(arguments, 'arguments')
    //     for (let i = 0; i < arguments.length; i++) {
    //       subArgs.push(arguments[i]);
    //     }
    //     // 判断参数的长度是否已经满足函数所需参数的长度
    //     if (subArgs.length >= length) {
    //       // 如果满足，执行函数
    //       return fn.apply(this, subArgs);
    //     } else {
    //       // 如果不满足，递归返回科里化的函数，等待参数的传入
    //       return curry.call(this, fn, subArgs);
    //     }
    //   }
    // }


    // curry(1)(2)(3)                // 6
    // curry(1, 2, 3)(4)             // 10
    // curry(1)(2)(3)(4)(5)          // 15
    // curry(2, 6)(1)     
    // es6 实现
    // function curry(fn, ...args) {
    //   return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args);
    // }
  </script>
</body>
</html>